﻿using System;
using System.Collections.Generic;

namespace Snake.FSM
{
    /// <summary>
    /// 有限状态机
    /// </summary>
    /// <typeparam name="TStateKey">状态的Key</typeparam>
    /// <typeparam name="TOwner">拥有者</typeparam>
    public class BaseFsm<TStateKey, TOwner>
    {
        public TStateKey CurrentState;
        private FsmState<TStateKey, TOwner> _currentState;

        private FsmState<TStateKey, TOwner> _defaultState;

        private bool _isInit;

        private readonly TOwner _owner;
        private readonly Dictionary<TStateKey, FsmState<TStateKey, TOwner>> _states;

        public BaseFsm(TOwner owner)
        {
            _owner = owner;
            _states = new Dictionary<TStateKey, FsmState<TStateKey, TOwner>>();
        }

        public void Update()
        {
            Init();

            if (_currentState == null)
            {
                return;
            }

            _currentState.OnUpdate(_owner);
            if (!_currentState.CheckCondition(_owner, out var state))
            {
                return;
            }

            ChangeState(state);
            CurrentState = state;
        }

        public void SetDefault(TStateKey stateKey)
        {
            if (!_states.TryGetValue(stateKey, out var state))
            {
                return;
            }

            _defaultState = state;
            _currentState = _defaultState;
            CurrentState = stateKey;
        }

        public void AddState(TStateKey stateKey)
        {
            _states.Add(stateKey, new FsmState<TStateKey, TOwner>());
        }

        public void AddState(TStateKey stateKey, FsmState<TStateKey, TOwner> state)
        {
            if (state == null)
            {
                return;
            }

            _states.Add(stateKey, state);
        }

        public FsmState<TStateKey, TOwner> GetState(TStateKey stateEnum)
        {
            return _states.GetValueOrDefault(stateEnum);
        }

        public void BindEnterAction(TStateKey stateEnum, Action<TOwner> action)
        {
            if (!_states.TryGetValue(stateEnum, out var state))
            {
                return;
            }

            state.BindEnterAction(action);
        }

        public void BindUpdateAction(TStateKey stateEnum, Action<TOwner> action)
        {
            if (!_states.TryGetValue(stateEnum, out var state))
            {
                return;
            }

            state.BindUpdateAction(action);
        }

        public void BindExitAction(TStateKey stateEnum, Action<TOwner> action)
        {
            if (!_states.TryGetValue(stateEnum, out var state))
            {
                return;
            }

            state.BindExitAction(action);
        }

        public void AddCondition(TStateKey stateEnum, FsmCondition<TOwner> condition, TStateKey toState)
        {
            if (!_states.TryGetValue(stateEnum, out var state))
            {
                return;
            }

            state.AddCondition(condition, toState);
        }

        private void Init()
        {
            if (_isInit)
            {
                return;
            }

            _currentState.OnEnter(_owner);
            _isInit = true;
        }

        private void ChangeState(TStateKey stateKey)
        {
            if (!_states.TryGetValue(stateKey, out var state))
            {
                return;
            }

            _currentState.OnExit(_owner);
            state.OnEnter(_owner);
            _currentState = state;
        }
    }
}